/**
 * 
 */
package com.gr.experimental.model.impl.distr;

import java.util.List;
import java.util.Map;

import com.gr.experimental.model.Distribution;
import com.gr.experimental.model.RVClass;
import com.gr.experimental.model.RVClassInfluence;
import com.gr.experimental.model.RVState;
import com.gr.experimental.model.UID;
import com.gr.experimental.model.impl.distr.operations.PerformActionException;

/**
 * @author Karol Banczyk
 *
 */
public class LocalDynamicDistribution implements Distribution {

	
	List<RVClass> parents;
	List<UID> domain; 
	Action definition;
	Context superContext;
	Map<String, Descriptor> descriptorMap;
	Descriptor returnDescriptor;
	
	public LocalDynamicDistribution(
			List<RVClass> parents, 
			List<UID> domain,
			Action definition,
			Context superContext,
			Map<String, Descriptor> descriptorMap,
			Descriptor returnDescriptor) {
		this.parents = parents;
		this.domain = domain;
		this.definition = definition;
		this.superContext = superContext;
		// TODO: add check, that there are no modifiable variables in super context
		// unless you want some variables shared between different contexts
		this.descriptorMap = descriptorMap;
		this.returnDescriptor = returnDescriptor;
		assert definition != null;
	}

	/**
	 * {@inheritDoc}
	 * @see com.gr.experimental.model.Distribution#getConditionRVClasses()
	 */
	@Override
	public List<RVClass> getConditionRVClasses() {
		return parents;
	}

	/**
	 * {@inheritDoc}
	 * @see com.gr.experimental.model.Distribution#getDomain()
	 */
	@Override
	public List<UID> getDomain() {
		return domain;
	}

	/**
	 * {@inheritDoc}
	 * @throws DistributionException 
	 * @see com.gr.experimental.model.Distribution#getProbabilities(java.util.List)
	 */
	@Override
	public double[] getProbabilities(List<RVClassInfluence> partialWorldState) throws DistributionException {
		
		//TODO: add some result caching
		// For the sake of caching we have to
		// count influence counts here.
		// It is therefore most reasonable
		// to put the count results directly into the cache
		// as they are needed later.		
 
		
		// create execution context
		ContextImpl ctx = new ContextImpl(superContext,descriptorMap, returnDescriptor);

		// create descriptors with rv state influence counts
		// this place can probably be optimised quite easily 
		if (partialWorldState != null) {
			for (RVClassInfluence influence: partialWorldState) {
				influence.getRVStateInfluenceCounts();
				RVClass rvClass = influence.getRVClass();
				List<RVState> classDomain = rvClass.getDomain();
				if (classDomain.size() != influence.getRVStateInfluenceCounts().length) {
					throw new IllegalArgumentException("Domain size doesn't match influence count length for class: " + rvClass);
				}

				// count the number of rv class instances
				ctx.addDescriptor(
						new DescriptorImpl("count(" + rvClass.getName() + ")", Integer.class,
							DescriptorType.Variable, false,influence.getRvClassInfluenceCount())
							);
				
				// count the number or rv class states instances
				for (int i=0; i < classDomain.size(); i++) {
					ctx.addDescriptor(
						new DescriptorImpl("count(" + classDomain.get(i).getName() + ")", Integer.class,
							DescriptorType.Variable, false,influence.getRVStateInfluenceCounts()[i]) 
							);
				}
				
			}
		}
		
		ctx.setVariableValue(
				ctx.getReturnDescriptor().getSymbolName(),
				new Double[domain.size()]);
		
		try {
			definition.perform(ctx);
		} catch (PerformActionException e) {
			throw new DistributionException(e);
		}
		Double[] prob = (Double[]) ctx.getVariableValue("prob");
		double[] probability = new double[prob.length];
		for (int i=0; i< prob.length; i++) {
			probability[i] = prob[i];
		}
		return probability;
	}

	/**
	 * {@inheritDoc}
	 * @throws DistributionException 
	 * @see com.gr.experimental.model.Distribution#getProbabilities()
	 */
	@Override
	public double[] getProbabilities() throws DistributionException {
		return getProbabilities(null);
	}


	/**
	 * @return the parents
	 */
	List<RVClass> getParents() {
		return this.parents;
	}

}
